package com.knowvoyage.knowvoyageserver.util;

import com.knowvoyage.knowvoyageserver.pojo.vo.Result;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;

public class ChatClient {
    private final WebClient webClient;

    public ChatClient() {
        String API_URL = "http://127.0.0.1:5000";
        this.webClient = WebClient.builder()
                .baseUrl(API_URL)
                .codecs(configurer ->
                        configurer.defaultCodecs().maxInMemorySize(256 * 1024 * 1024))//缓冲池
                .build();
    }

    //非流式返回，Mono<String>前端可直接接受
    public Mono<String> getChatMessage(ChatRequest request) {
        return webClient.post()
                .uri("/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(request)
                .retrieve()
                .bodyToMono(String.class);
    }

    //流式返回，Flux<String>前端需遍历接受
    public Flux<String> getChatMessageStream(ChatRequest request) {
        return webClient.post()
                .uri("/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.TEXT_EVENT_STREAM)//流式返回需加这样的请求头
                .bodyValue(request)
                .retrieve()
                .bodyToFlux(String.class);
    }

    //加载历史记录
    public Mono<String> loadMessages(LinkedHashMap<String, Object> body) {
        return webClient.post()
                .uri("/load")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(String.class);
    }

    public Mono<String> ocr(Path tempFile) {
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        builder.part("file", new FileSystemResource(tempFile));
        Mono<String> mono = null;
        try {
             mono = webClient.post()
                    .uri("ocr")
                    .contentType(MediaType.MULTIPART_FORM_DATA)
                    .body(BodyInserters.fromMultipartData(builder.build()))
                    .retrieve()
                    .bodyToMono(String.class);
        }catch (Exception e) {
            System.out.println(e.getMessage());
        }

        return mono.flatMap(ocrResponse -> {
                try {
                    JSONObject jsonObject = new JSONObject(ocrResponse);

                    JSONArray result = jsonObject.getJSONArray("result");
                    StringBuilder ans = new StringBuilder();
                    for (int i = 0; i < result.length(); i++) {
                        JSONObject obj = result.getJSONObject(i);
                        ans.append(obj.getString("text")).append(" ");
                    }

                    return Mono.just(ans.toString().trim());

                } catch (JSONException e) {
                    return Mono.just("解析错误");
                }
            })
            .publishOn(Schedulers.boundedElastic())
            .doFinally(signal -> {
                try {
                    Files.deleteIfExists(tempFile);
                } catch (IOException e) {
                    System.err.println("删除临时文件失败：" + e.getMessage());
                }
            });
    }

    public Mono<String> chat(Map<String, Object> request) {
        Map<String, Object> mp = new LinkedHashMap<>();
        mp.put("message", request.get("ask"));
        mp.put("mode", request.get("mode") == "" || request.get("mode") == null ? "0" : request.get("mode"));
        mp.put("singleTurn", true);
        mp.put("model", request.get("model"));
        return webClient.post()
                .uri("/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(mp)
                .retrieve()
                .bodyToMono(String.class);
    }

    public Flux<String> stream_chat(Map<String, Object> request) {
        Map<String, Object> mp = new LinkedHashMap<>();
        mp.put("message", request.get("ask"));
        mp.put("mode", request.get("mode") == "" || request.get("mode") == null ? "0" : request.get("mode"));
        mp.put("singleTurn", true);
        mp.put("model", request.get("model"));
        return webClient.post()
                .uri("/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.TEXT_EVENT_STREAM)
                .bodyValue(mp)
                .retrieve()
                .bodyToFlux(String.class);
    }

    public Mono<Result<String>> question(Map<String, Object> request) {
        Map<String, Object> mp = new LinkedHashMap<>();
        String subject = (String) request.get("subject");
        int difficulty = (int) request.get("difficulty");
        String ask = "请生成一个" + difficulty + "难度的" + subject + "题目，题目要求如下：" +
                "1. 题目必须是" + subject + "相关的，不能是其他学科的题目。" +
                "2. 题目必须是" + difficulty + "难度的，不能是简单或困难的题目。难度从1到5逐级递增。" +
                "3. 题目是选择题，且在option中把四个选项写出来。" +
                "4. 题目内容必须是中文的，不能是英文的。" +
                "5. 题目内容、题目答案和题目解析必须是完整的，不能是省略的。" +
                "6. 题目内容、题目答案和题目解析必须是正确的，不能是错误的。" +
                "7. 返回给我的内容必须是json格式，格式如下：" +
                "{" +
                "    \"question\": \"题目内容\"," +
                "    \"options\": [\"A. 选项1\", \"B. 选项2\", \"C. 选项3\", \"D. 选项4\"]," +
                "    \"answer\": \"题目答案\"," +
                "    \"explanation\": \"题目答案的解析\"" +
                "}" +
                "8. 只输出{}和里面的内容，不要有别的输出。" +
                "9. answer从ABCD中选一个，只需要是ABCD中一项就行。";


        mp.put("singleTurn", true);
        mp.put("message", ask);
        return webClient
                .post()
                .uri("/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(mp)
                .retrieve()
                .bodyToMono(String.class)
                .flatMap(res->{
                    try {
                        JSONObject js = new JSONObject(res);
                        String response = js.getString("response");
                        int pos = response.indexOf("</think>");
                        if (pos != -1) {
                            response = response.substring(pos + 8);
                        }
                        return Mono.just(Result.success(response));
                    } catch (JSONException e) {
                        String error = """
                                {
                                    "question": "",
                                    "options": [],
                                    "answer": "",
                                    "explanation": ""
                                }
                                """;
                        return Mono.just(Result.error(500, error));
                    }
                });
    }
}
